//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//==========================================================================
// Copyright (c) 2000-2004,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

/*
 * Copyright (c) 1997, 1998
 *    Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

//#pragma warning(disable:4133)  //subtract two pointers of different types
//#pragma warning(disable:4700)

#include <eladef.h>
#include <util/kprint.h>
#include "rtl8139.h"
#include <string.h>
#include <ddk.h>
#include <ite8172.h>

/*
 * Default to using PIO access for this driver. On SMP systems,
 * there appear to be problems with memory mapped mode: it looks like
 * doing too many memory mapped access back to back in rapid succession
 * can hang the bus. I'm inclined to blame this on crummy design/construction
 * on the part of RealTek. Memory mapped mode does appear to work on
 * uniprocessor systems though.
 */

//static int Rtl8139_Encap(netdrv *, struct mbuf *);
static void rtl_8139_rxeof(netdrv *);
static void rtl_8139_txeof(netdrv *);
extern void rtl_8139_stop(netdrv *);
extern void rtl_8139_stutdown(netdrv *pThis);
static void rtl_8139_eeprom_putbyte(netdrv *, int);
static void rtl_8139_eeprom_getword(netdrv *, int, UInt16 *);
static void rtl_8139_read_eeprom(netdrv *, unsigned char *, int, int, int);

//static UInt8 Rtl8139_Calchash(unsigned char *);
static void rtl_8139_setmulti(netdrv *);
static void rtl_8139_reset(netdrv *);
static int  rtl_8139_var_init(netdrv *);

static int  rtl_8139initalize(netdrv *  pThis);
int  rtl_8139xmit(netdrv*  pThis);
//ECode  rtl8139Ioctl(netdrv* pThis, UInt32 command);
static void  __cdecl irhan(irq_t , void *, InterruptContext *);
//Boolean   RTL8139Interrupt(void*  pThis);

//void   RTL8139Watchdog(netdrv* pThis);

static unsigned int mroundup2order(unsigned int u);

#define EE_SET(x)                           \
    CSR_WRITE_1(pThis, RL_EECMD,            \
        CSR_READ_1(pThis, RL_EECMD) | x)

#define EE_CLR(x)                           \
    CSR_WRITE_1(pThis, RL_EECMD,            \
        CSR_READ_1(pThis, RL_EECMD) & ~x)

void delay(int n)
{
    n = n*0x03f;
    while (n > 0) {
        n--;
    }
}

/*
 * Send a read command and address to the EEPROM, check for ACK.
 */
static void
rtl_8139_eeprom_putbyte(netdrv *pThis, int addr)
{
    register int d, i;

    d = addr | RL_EECMD_READ;

    /*
     * Feed in each bit and strobe the clock.
     */
    for (i = 0x400; i; i >>= 1) {
        if (d & i) {
            EE_SET(RL_EE_DATAIN);
        }
        else {
            EE_CLR(RL_EE_DATAIN);
        }
        delay(100);
        EE_SET(RL_EE_CLK);
        delay(150);
        EE_CLR(RL_EE_CLK);
        delay(100);
    }
    return;
}

/*
 * Read a word of data stored in the EEPROM at address 'addr.'
 */
static void
rtl_8139_eeprom_getword(netdrv *pThis, int addr, UInt16 *dest)
{
    register int  i;
    UInt16        word = 0;

    /*
     * Enter EEPROM access mode.
     */
    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);

    /*
     * Send address of word we want to read.
     */
    rtl_8139_eeprom_putbyte(pThis, addr);

    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);

    /*
     * Start reading bits from EEPROM.
     */
    for (i = 0x8000; i; i >>= 1) {
        EE_SET(RL_EE_CLK);
        delay(50);
        if (CSR_READ_1(pThis, RL_EECMD) & RL_EE_DATAOUT)
            word |= i;
        EE_CLR(RL_EE_CLK);
        delay(50);
    }

    /* Turn off EEPROM access mode. */
    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_OFF);

    *dest = word;

    return;
}

u_short __stdcall n2hs(u_short netshort)
{
    return (u_short)(((netshort & 0xff) << 8) | ((netshort & 0xff00) >> 8));
}

/*
 * Read a sequence of words from the EEPROM.
 */
static void
rtl_8139_read_eeprom(netdrv *pThis, unsigned char * dest, int off,
                int cnt, int swap)
{
    int      i;
    UInt16   word = 0, *ptr;

    for (i = 0; i < cnt; i++) {
        rtl_8139_eeprom_getword(pThis, off + i, &word);
        ptr = (UInt16 *)(dest + (i * 2));
        if (swap)
            *ptr = n2hs(word);
        else
            *ptr = word;
    }

    return;
}

/*
 * Calculate CRC of a multicast group address, return the upper 6 bits.
 */
//static UInt8
//Rtl8139_Calchash(unsigned char * addr)
//{
//    UInt32    crc, carry;
//    int       i, j;
//    UInt8     c;
//
//    /* Compute CRC for the address value. */
//    crc = 0xFFFFFFFF; /* initial value */
//
//    for (i = 0; i < 6; i++) {
//        c = *(addr + i);
//        for (j = 0; j < 8; j++) {
//            carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
//            crc <<= 1;
//            c >>= 1;
//            if (carry)
//                crc = (crc ^ 0x04c11db6) | carry;
//        }
//    }
//
//    /* return the filter bit position */
//    return(crc >> 26);
//}

/*
 * Program the 64-bit multicast hash filter.
 */
static void
rtl_8139_setmulti(netdrv *pThis)
{
//    struct ifnet_             *ifp = &pThis->if_handle->arpcom.ac_if;
//    struct arpcom_            *ac = &pThis->if_handle->arpcom;
//    struct ether_multi       *enm;
//    struct ether_multistep   step;
//    int            h = 0;
//    UInt32         hashes[2] = { 0, 0 };
    UInt32         rxfilt;
//    int            mcnt = 0;

    rxfilt = CSR_READ_4(pThis, RL_RXCFG);

//    if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC)
    {
        rxfilt |= RL_RXCFG_RX_MULTI;
        CSR_WRITE_4(pThis, RL_RXCFG, rxfilt);
        CSR_WRITE_4(pThis, RL_MAR0, 0xFFFFFFFF);
        CSR_WRITE_4(pThis, RL_MAR4, 0xFFFFFFFF);
        return;
    }

    /* first, zot all the existing hash bits */
//    CSR_WRITE_4(pThis, RL_MAR0, 0);
//    CSR_WRITE_4(pThis, RL_MAR4, 0);
//
//    /* now program new ones */
//    ETHER_FIRST_MULTI(step, ac, enm);
//    while (enm != NULL) {
//        mcnt++;
//        h = Rtl8139_Calchash(enm->enm_addrlo);
//        if (h < 32)
//            hashes[0] |= (1 << h);
//        else
//            hashes[1] |= (1 << (h - 32));
//        mcnt++;
//        ETHER_NEXT_MULTI(step, enm);
//     }
//
//    if (mcnt)
//        rxfilt |= RL_RXCFG_RX_MULTI;
//    else
//        rxfilt &= ~RL_RXCFG_RX_MULTI;
//
//    CSR_WRITE_4(pThis, RL_RXCFG, rxfilt);
//    CSR_WRITE_4(pThis, RL_MAR0, hashes[0]);
//    CSR_WRITE_4(pThis, RL_MAR4, hashes[1]);
//
//    return;
}

static void rtl_8139_reset(netdrv *pThis)
{
    register int        i;

    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_RESET);

    for (i = 0; i < RL_TIMEOUT; i++) {
        delay(10);
        if (!(CSR_READ_1(pThis, RL_COMMAND) & RL_CMD_RESET))
            break;
    }
    if (i == RL_TIMEOUT)
        kprintf("Error:rl%d: reset never completed!\n", pThis->rl_unit);

    return;
}

static unsigned int mroundup2order(unsigned int u)
{
    if (!(u > 0)) {
        DebugBreak();
    }

    if (u == 1) {
        return 0;
    }
    if (u > 1 && u <= 2) {
        return 1;
    }
    if (u > 2 && u <= 4) {
        return 2;
    }
    if (u > 4 && u <= 8) {
        return 3;
    }
    if (u > 8 && u <= 16) {
        return 4;
    }
    if (u > 16 && u <= 32) {
        return 5;
    }
    if (u > 32 && u <= 64) {
        return 6;
    }
    if (u > 64 && u <= 128) {
        return 7;
    }
    if (u > 128 && u <= 256) {
        return 8;
    }
    if (u > 256 && u <= 512) {
        return 9;
    }
    if (u > 512 && u <= 1024) {
        return 10;
    }
    if (u > 1024 && u <= 2048) {
        return 11;
    }
    if (u > 2048 && u <= 4096) {
        return 12;
    }
    if (u > 4096 && u <= 8192) {
        return 13;
    }
    if (u > 8192 && u <= 16384) {
        return 14;
    }
    if (u > 16384 && u <= 32768) {
        return 15;
    }
    if (u > 32768 && u <= 65536) {
        return 16;
    }
    if (u > 65536 && u <= 131072) {
        return 17;
    }
    if (u > 131072 && u <= 262144) {
        return 18;
    }
    if (u > 262144 && u <= 524288) {
        return 19;
    }
    if (u > 524288 && u <= 1048576) {
        return 20;
    }
    if (u > 1048576 && u <= 2097152) {
        return 21;
    }
    if (u > 2097152 && u <= 4194304) {
        return 22;
    }
    if (u > 4194304 && u <= 8388608) {
        return 23;
    }
    if (u > 8388608 && u <= 16777216) {
        return 24;
    }
    if (u > 16777216 && u <= 33554432) {
        return 25;
    }
    return 26;
}

//
// TODO: more than nend
//
uint32_t    uDMATest[0x10000];
static int rlinit(netdrv *pThis, int unit)
{

    int       rl_did = 0;
    int i = 0;

    /* Reset the adapter. */
    rtl_8139_reset(pThis);

    /*
     * Get station address from the EEPROM.
     */
    rtl_8139_read_eeprom(pThis, (unsigned char *)&(pThis->eaddr[0]),
                    RL_EE_EADDR, 3, 0);

    /*
     * A RealTek chip was detected. Inform the world.
     */
    kprintf("rl%d: Ethernet address: ", unit);
    for (i = 0; i <= ETHER_ADDR_LEN-1; i++) {
        kprintf("%02x ", pThis->eaddr[i]);
    }
    kprintf("\n");

    pThis->rl_unit = unit;
    pThis->cur = pThis->last = 0;

    /*
     * Now read the exact device type from the EEPROM to find
     * out if it's an 8129 or 8139.
     */
    rtl_8139_read_eeprom(pThis, (unsigned char *)&rl_did, RL_EE_PCI_DID, 1, 0);

    if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 ||
        rl_did == DELTA_DEVICEID_8139 || rl_did == ADDTRON_DEVICEID_8139 ||
        rl_did == DLINK_DEVICEID_530TXPLUS)
        pThis->rl_type = RL_8139;
    else if (rl_did == RT_DEVICEID_8129)
        pThis->rl_type = RL_8129;
    else {
        kprintf("Error:rl%d: unknown device ID: %x\n", unit, rl_did);
        return -1;
    }
    kprintf("rl%d: Device ID: %x\n", unit, rl_did);

    {
#ifdef _ETHER_DEBUG_
        kprintf("%d %d\n", RL_RXBUFLEN + 1518, mroundup2order((RL_RXBUFLEN
        + 1518)/(4*1024)));
#endif

#if 0
        char * pAddr;
        pAddr = (char*)DzAllocKernelPages(
            mroundup2order((RL_RXBUFLEN + 1518 + 4*1024 - 1)/(4*1024)));
        if (pAddr == NULL) {
            kprintf("Error: Could not malloc.\n");
        }

        pThis->dmaaddr = (unsigned char *)pAddr;
#else
        pThis->dmaaddr = (uint8_t *)uDMATest;
#endif
    }
    return 0;
}

static netdrv * rtl;

extern netdrv *rtl_8139new(int * pPortBase, int * pIrq, int * pMedia)
{
    netdrv     *pThis;
    static int   unit = 0;

//unsigned int r;
//unsigned int confaddr;
//unsigned int offset = 0;
//unsigned int nDeviceNum = 2;
////    *((volatile unsigned short *)0xb4015800) &= ~0x200;
//
//while (nDeviceNum <= 20)
//{
//kprintf(">>>>>>>>>>>>>Device Number : %d>>>>>>>>>>>>>>>\n", nDeviceNum);
//    for (int i = 0; i < 64; ++i)
//    {
//        confaddr = nDeviceNum << 11;
//        Outl(KSEG1ADDR(0x18004000),confaddr+offset);
//
//        r=Inl(KSEG1ADDR(0x18004004));
//
//        kprintf("%08x : %08x\n", confaddr+offset, r);
//
//        offset += 4;
//    }
//
//    nDeviceNum++;
//// r = Inl(KSEG1ADDR(0x14016010));
////
//// kprintf("%08x:%02x\n", 0x14016000,r);
//}
//while (1);

    pThis = (netdrv *)malloc(sizeof(netdrv));
    if (pThis == NULL) {
        kprintf("Error:Could not allocate %d bytes\n", sizeof *pThis);
        return NULL;
    }
    bzero(pThis, sizeof(netdrv));

    pThis->iobase = *pPortBase;
    pThis->irq = *pIrq;

    AddIt8172Device(pThis->irq, &irhan, pThis);

    if (rlinit(pThis, unit)) {
        kprintf("Init Card Error\n");
        return NULL;
    }
    unit++;
    rtl = pThis;
    return pThis;
}

ECode Ist(void *arg)
{
    netdrv *pThis = (netdrv*)arg;
    unsigned short status;

    WaitResult wr;
    while (1) {
        pThis->pISRevent->Wait(&wr, NULL);

        for (;;) {
            status = CSR_READ_2(pThis, RL_ISR);

            if (status)
                CSR_WRITE_2(pThis, RL_ISR, status);

            if ((status & RL_INTRS) == 0)
                break;

            if ((status & RL_ISR_RX_OK) || (status & RL_ISR_RX_ERR)) {
                rtl_8139_rxeof(pThis);
            }

            if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) {
                rtl_8139_txeof(pThis);
            }

            if (status & RL_ISR_SYSTEM_ERR) {
                rtl_8139_reset(pThis);
                rtl_8139initalize(pThis);
            }
        }

        if (!QUEUE_EMPTY(pThis->netp, depart)) {
            /* Data in Tx buffer waiting for transimission */
            rtl_8139xmit(pThis);
        }
        /* Re-enable interrupts. */
        CSR_WRITE_2(pThis, RL_IMR, RL_INTRS);
    }
    return NOERROR;
}

extern int
rtl_8139_connect(void * softc, void** hardc)
{
    hardepen* hard = (hardepen *)softc;
    rtl->pRWevent = new DzEvent;
    rtl->pISRevent = new DzEvent;
    rtl->netp = hard->netp;
    rtl->pNfirstbuf = hard->pNfirstbuf;
    rtl->netno = hard->netno;
    *hardc = rtl;
    //copy mac address
    memcpy(&(hard->netp->id.c[0]), &(rtl->eaddr[0]), 6);

    ECode ec = DzCreateKernelThread(Ist, (void*)rtl, 0
            , ThreadPriorityRank_Normal, NULL);
    if (FAILED(ec)) {
        kprintf("*ERROR* failed to create kernelthread\n");
    }

    rtl_8139_reset(rtl);
    rtl_8139initalize(rtl);
    return 0;
}

/*
 * Initialize the transmit descriptors.
 */
static int
rtl_8139_var_init(netdrv *pThis)
{
    int i;
    for (i = 0; i < RL_TX_LIST_CNT; i++) {
        CSR_WRITE_4(pThis, RL_TXADDR0 + (i * sizeof(UInt32)), 0x0);
    }
    pThis->cur = pThis->last = 0;
    return(0);
}

int
rtl_8139initalize(netdrv* pThis)
{
    int            i;
    UInt32        rxcfg = 0;
    Address addr;

    /*
     * Cancel pending I/O and free all RX/TX buffers.
     */
    rtl_8139_stop(pThis);

    /* Init our MAC address */
    for (i = 0; i < ETHER_ADDR_LEN; i++) {
        CSR_WRITE_1(pThis, RL_IDR0 + i, pThis->eaddr[i]);
    }

    /* Init the RX buffer pointer register. */
    {
        UInt32 p = 0;

        addr = (Address)pThis->dmaaddr;
        p = (UInt32)PHYSADDR((char *)addr);

#ifdef _ETHER_DEBUG_
        kprintf("\n BBB addr = %08x p = %08x\n", addr, p);
#endif

        CSR_WRITE_4(pThis, RL_RXADDR, p);
    }

    /* Init descriptors. */
    rtl_8139_var_init(pThis);

    /*
     * Enable transmit and receive.
     */
    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);

    /*
     * Set the initial TX and RX configuration.
     */
    CSR_WRITE_4(pThis, RL_TXCFG, RL_TXCFG_CONFIG);
    CSR_WRITE_4(pThis, RL_RXCFG, RL_RXCFG_CONFIG);

    /* Set the individual bit to receive frames for this host only. */
    rxcfg = CSR_READ_4(pThis, RL_RXCFG);
    rxcfg |= RL_RXCFG_RX_INDIV;

    /* If we want promiscuous mode, set the allframes bit. */
//    if (ifp->if_flags & IFF_PROMISC) {
//        rxcfg |= RL_RXCFG_RX_ALLPHYS;
//        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
//    } else
    {
        rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
    }

    /*
     * Set capture broadcast bit to capture broadcast frames.
     */
//    if (ifp->if_flags & IFF_BROADCAST)
    {
        rxcfg |= RL_RXCFG_RX_BROAD;
        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
    }
//     else {
//        rxcfg &= ~RL_RXCFG_RX_BROAD;
//        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
//    }

    /*
     * Program the multicast filter, if necessary.
     */
    rtl_8139_setmulti(pThis);

    /*
     * Enable interrupts.
     */
    CSR_WRITE_2(pThis, RL_IMR, RL_INTRS);

    /* Set initial TX threshold */
    pThis->rl_txthresh = RL_TX_THRESH_INIT;

    /* Start RX/TX process. */
    CSR_WRITE_4(pThis, RL_MISSEDPKT, 0);

    /* Enable receiver and transmitter. */
    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);

    CSR_WRITE_1(pThis, RL_CFG1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX);

    return 0;
}

static MESS *
NgetbufIR(netdrv *pThis) /* from interrupt level */
{
    register MESS *mp;
    MESS *Nfirstbuf;

    bool_t t = DzDisablePreemption();
    Nfirstbuf = *(pThis->pNfirstbuf);

    if ((mp = Nfirstbuf) == 0) {
        // BUGBUG: memory leak
        DebugBreak();
        return 0;
    }
////#if NTRACE > 1
//    kprintf("> > >getIR %08x_%08x\n", *(pThis->pNfirstbuf), mp->next);
////#endif
    *(pThis->pNfirstbuf) = mp->next;
#if NTRACE > 0
    if (mp->id != bFREE) {
        Npanic("NgetbufIR id");
    }
#endif
    mp->id = bALLOC;
    mp->next = 0;                 /* NULL terminate buffer link */
    DzRestorePreemption(t);

    return mp;
}

void NrelbufIR(MESS * mp, netdrv * pThis) /* from interrupt level */
{
    MESS * Nfirstbuf;

    bool_t t = DzDisablePreemption();
    Nfirstbuf = *(pThis->pNfirstbuf);
#if NTRACE > 0
    if ((mp->id != bALLOC) && (mp->id != bRELEASE)) {
        Npanic("NrelbufIR id");
    }
#endif
    mp->id     = bFREE;
    mp->offset = boRELEASED;
    mp->next   = Nfirstbuf;
////#if NTRACE > 3
//    kprintf("> > > relIR %08x_%08x\n", *(pThis->pNfirstbuf), mp);
////#endif
    *(pThis->pNfirstbuf)  = mp;
    DzRestorePreemption(t);
}

static ipl_t old_ipl;
extern void _EnableIrq()
{
    DzLowerCurrentIpl(old_ipl);
//    kprintf("open :-))\n");
}

extern void _DisableIrq()
{
    old_ipl = DzRaiseCurrentIpl(IPL10);
//    kprintf("close :-((\n");
}

void __cdecl irhan(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    netdrv * pThis = (netdrv *)pvDevice;
    /* Disable interrupts. */
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);
    pThis->pISRevent->NotifyByIsr();
}

int rtl_8139xmit(netdrv* pThis)
{
    static unsigned char addr[2000];
    UInt32  p;
    MESS *  mess;
    int     len;
lab:
    if (QUEUE_EMPTY(pThis->netp, depart)) {
        pThis->netp->hwflags = 0;
    }
    else {
        QUEUE_OUT(pThis->netp, depart, mess);
        /* Check to see if we really want to transmit
           this frame.  It is possible for USNET to
           mark a frame in the depart queue as cancelled
           by changing the value of mess->offset to a
           value other than netno.  For example, if a
           frame times out waiting for an ARP reply,
           NetTask() changes offset to boTXDONE and
           releases the message buffer back into the
           pool.  If a frame has been cancelled, go on to
           the next frame in the depart queue.  */
        if (mess->offset != pThis->netno)
            goto lab;
        len = mess->mlen - MESSH_SZ;
        /* Set up the frame for transmission.  The frame
           starts at (char *)mess + MESSH_SZ and is len
           bytes long.  Note that some ethernet controllers
           will automatically pad out frames that are
           shorter than the minimum ethernet frame size,
           others may not, and so len may need to be
           adjusted.  */

        memset((void*)addr, 0, sizeof(addr));
        memcpy((void*)addr, (void*)((char *)mess + MESSH_SZ), len);

        //PHYSADDR only transfer kernel addr
        p = (UInt32)PHYSADDR((char *)addr);

        dma_cache_wback_inv((unsigned long)addr, 2000);
        CSR_WRITE_4(pThis, (RL_TXADDR0 + 4 * pThis->cur), p);

        //kprintf("len= %d\n", len);
        CSR_WRITE_4(pThis, (RL_TXSTAT0 + 4 * pThis->cur),
             RL_TXTHRESH(pThis->rl_txthresh) |
             (len < 1514 ? (len > 60 ? len : 60) : 1514));

        RL_INC(pThis->cur);
        /* Once the hardware has been programmed to send a
           frame, the offset field is set to the special
           value boTXDONE to indicate to other layers that
           the device driver no longer has control of the
           message buffer. */
        mess->offset = boTXDONE;
        /* Check the id associated with the message buffer.
           If a frame is transmitted within this ISR, then
           it is the responsibility of the ISR to release
           the buffer if it is no longer needed by other
           layers in the stack.  The following code takes
           care of this. */

        if (mess->id <= bWACK) {
            if (mess->id == bRELEASE) {
                mess->id = bALLOC;
                NrelbufIR(mess, pThis);
            }
        }
        else {
            //WAITNOMORE_IR(SIG_WN(netno));
            pThis->pRWevent->Notify(SIG_WN(pThis->netno));
        }
    }

    /*
         * Set a timeout in case the chip goes out to lunch.
         */
         //BUGBUG:
//        ifp->if_timer = 5;

    return 0;
}

/*
 * A frame has been uploaded: pass the resulting mbuf chain up to
 * the higher level protocols.
 *
 * You know there's something wrong with a PCI bus-master chip design
 * when you have to use m_devget().
 *
 * The receive operation is badly documented in the datasheet, so I'll
 * attempt to document it here. The driver provides a buffer area and
 * places its base address in the RX buffer start address register.
 * The chip then begins copying frames into the RX buffer. Each frame
 * is preceeded by a 32-bit RX status word which specifies the length
 * of the frame and certain other status bits. Each frame (starting with
 * the status word) is also 32-bit aligned. The frame length is in the
 * first 16 bits of the status word; the lower 15 bits correspond with
 * the 'rx status register' mentioned in the datasheet.
 *
 * Note: to make the Alpha happy, the frame payload needs to be aligned
 * on a 32-bit boundary. To achieve this, we cheat a bit by copying from
 * the ring buffer starting at an address two bytes before the actual
 * data location. We can then shave off the first two bytes using m_adj().
 * The reason we do this is because m_devget() doesn't let us specify an
 * offset into the mbuf storage space, so we have to artificially create
 * one. The ring is allocated in such a way that there are a few unused
 * bytes of space preceecing it so that it will be safe for us to do the
 * 2-byte backstep even if reading from the ring at offset 0.
 */
//inline ECode SetBreakPoint(
//        UInt32 uNo, BreakPointType type, void *pvAddress, UInt32 uLength)
//{
//    return GetCurrentProcess()->SetBreakPoint(uNo, type, pvAddress, uLength);
//}

void
rtl_8139_rxeof(netdrv *pThis)
{
    int           total_len = 0;
    UInt32        rxstat;
    unsigned char *       rxbufpos;
    int           wrap = 0;
    UInt16        cur_rx;
    UInt16        limit;
    UInt16        rx_bytes = 0, max_bytes;
    MESS *        mess = NULL;
    char *        spt;

    /* 16 means 64bit sychronous singnal and 64bit buf ring*/
    cur_rx = (UInt16)((CSR_READ_2(pThis, RL_CURRXADDR) + 16) % RL_RXBUFLEN);

    /* Do not try to read past this point. */
    limit = (UInt16)(CSR_READ_2(pThis, RL_CURRXBUF) % RL_RXBUFLEN);

    if (limit < cur_rx)
        max_bytes = RL_RXBUFLEN - (cur_rx - limit);
    else
        max_bytes = limit - cur_rx;

    while ((CSR_READ_1(pThis, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) {
        rxbufpos = pThis->dmaaddr + cur_rx;

        dma_cache_inv((Address)rxbufpos, 4);

        rxstat = *(UInt32 *)rxbufpos;

        /*
             * Here's a totally undocumented fact for you. When the
             * RealTek chip is in the process of copying a packet into
             * RAM for you, the length will be 0xfff0. If you spot a
             * packet header with this value, you need to stop. The
             * datasheet makes absolutely no mention of this and
             * RealTek should be shot for this.
             */
        if ((UInt16)(rxstat >> 16) == RL_RXSTAT_UNFINISHED)
            break;

        if (!(rxstat & RL_RXSTAT_RXOK)) {
            rtl_8139initalize(pThis);
            return;
        }

        /* No errors; receive the packet. */
        total_len = rxstat >> 16;
            /* 4  means the spzce of  total_len in rx*/
        rx_bytes += total_len + 4;

        /*
             * XXX The RealTek chip includes the CRC with every
             * received frame, and there's no way to turn this
             * behavior off (at least, I can't find anything in
             * the manual that explains how to do it) so we have
             * to trim off the CRC manually.
             */
        total_len -= ETHER_CRC_LEN;

        /*
             * Avoid trying to read more bytes than we know
             * the chip has prepared for us.
             */
        if (rx_bytes > max_bytes)
            break;

        rxbufpos = pThis->dmaaddr +
                ((cur_rx + sizeof(UInt32)) % RL_RXBUFLEN);

        if (rxbufpos == (pThis->dmaaddr + RL_RXBUFLEN))
            rxbufpos = pThis->dmaaddr;

        wrap = (pThis->dmaaddr + RL_RXBUFLEN) - rxbufpos;
        
        // round down
        dma_cache_inv((Address)((Address)(rxbufpos - RL_ETHER_ALIGN) & ~3),
                          ((total_len + RL_ETHER_ALIGN + 3) & ~3));

        /* Check for room in queue */
        if (QUEUE_FULL(pThis->netp, arrive)) {
            goto lab4;
        }
            /* Packet is good, try to get a memory block */
        if ((mess = NgetbufIR(pThis)) == 0) {
lab4:
            goto lab11;
        }

        /* Packet and memory block are good, get packet from ring buffer */
        mess->mlen = total_len + MESSH_SZ;
        mess->offset = MESSH_SZ;
        mess->netno = pThis->netno;
        spt = (char *)((char *)mess + MESSH_SZ);

        //now, we got enough space to fill in
        memcpy(spt, rxbufpos, total_len);
        if (total_len > wrap) {
            memcpy(spt + wrap, pThis->dmaaddr, total_len - wrap);
        }
lab11:
        if (total_len > wrap) {
            cur_rx = (total_len - wrap + ETHER_CRC_LEN);
        }
        else {
            cur_rx += total_len + 4 + ETHER_CRC_LEN;
        }

        /*
             * Round up to 32-bit boundary.
             */
        cur_rx = (cur_rx + 3) & ~3;
        CSR_WRITE_2(pThis, RL_CURRXADDR, cur_rx - 16);

        //insert arrive queue.
        QUEUE_IN(pThis->netp, arrive, mess);

        //if the queue is waiting for data, now wake it up.
            //WAITNOMORE_IR(SIG_RN(netno));
        pThis->pRWevent->Notify(SIG_RN(pThis->netno));
    }
    return;
}

/*
 * A frame was downloaded to the chip. It's safe for us to clean up
 * the list buffers.
 */
static void
rtl_8139_txeof(netdrv *pThis)
{
    UInt32        txstat;
    int           oldthresh;

    /* Clear the timeout timer. */
    //BUGBUG:maybe the following comment  is a  mistake.
//        ifp->if_timer = 0;

    /*
     * Go through our tx list and free mbufs for those
     * frames that have been uploaded.
     */
    do {
        txstat = CSR_READ_4(pThis, RL_TXSTAT0);
        if (!(txstat & (RL_TXSTAT_TX_OK|
            RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) {
            kprintf(" error recover\n");
            goto exit;
        }

        if (!(txstat & RL_TXSTAT_TX_OK)) {

            if ((txstat & RL_TXSTAT_TXABRT) ||
                (txstat & RL_TXSTAT_OUTOFWIN)) {
                CSR_WRITE_4(pThis, RL_TXCFG, RL_TXCFG_CONFIG);
            }

            oldthresh = pThis->rl_txthresh;
             /* error recovery */
            rtl_8139_reset(pThis);
            rtl_8139initalize(pThis);
             /*
              * If there was a transmit underrun,
              * bump the TX threshold.
              */
            if (txstat & RL_TXSTAT_TX_UNDERRUN)
                pThis->rl_txthresh = oldthresh + 32;
            return;
        }
        RL_INC(pThis->last);
    } while (pThis->cur != pThis->last);
exit:
    return;
}

void rtl_8139_stutdown(netdrv *pThis)
{
    CSR_WRITE_1(pThis, RL_COMMAND, 0x00);
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);

    for (int i = 0; i <= 3; i++) {
//#if defined(_mips)
//        CSR_WRITE_1(pThis, RL_TXADDR0 + i, 0x0);
//#else
        kprintf("down0");
        CSR_WRITE_4(pThis, RL_TXADDR0 + i*4, 0x0);
        kprintf("down1\n");
//#endif
    }

    delete pThis->pRWevent;

    return;
}

/*
 * Stop the adapter and free any mbufs allocated to the
 * RX and TX lists.
 */
static void rtl_8139_stop(netdrv *pThis)
{
    //shut down timer
//    ifp->if_timer = 0;

    CSR_WRITE_1(pThis, RL_COMMAND, 0x00);
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);

    /*
     * Free the TX list buffers.     */
    for (int i = 0; i <= 3; i++) {
//#if defined(_mips)
        CSR_WRITE_1(pThis, RL_TXADDR0 + i, 0x0);
//#else
//kprintf("stop0");
//     CSR_WRITE_4(pThis, RL_TXADDR0 + i*4, 0x0);
//kprintf("stop1\n");
//#endif
    }

//    ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);

    return;
}
